/*****************************************************************************/
/**
 *  @file   Converter.cpp
 *  @author Naohisa Sakamoto
 */
/*----------------------------------------------------------------------------
 *
 *  Copyright (c) Visualization Laboratory, Kyoto University.
 *  All rights reserved.
 *  See http://www.viz.media.kyoto-u.ac.jp/kvs/copyright/ for details.
 *
 *  $Id$
 */
/*****************************************************************************/
#include "Converter.h"
#include "Argument.h"
#include <iostream>
#include <kvs/File>
#include <kvs/Directory>
#include <kvs/AVSField>
#include <kvs/Timer>
#include <util/SPHFile.h>
#include <util/SPHFileList.h>
#include <util/Export.h>
#include <util/Write.h>


namespace
{

/*===========================================================================*/
/**
 *  @brief  Timer class.
 */
/*===========================================================================*/
class Timer
{
private:

    kvs::Timer m_timer; ///< timer
    float m_reading; ///< reading time
    float m_exporting; ///< exporting time
    float m_writing; ///< writing time

public:

    void start() { m_timer.start(); }
    void stopReading() { m_timer.stop(); m_reading = m_timer.sec(); }
    void stopExporting() { m_timer.stop(); m_exporting = m_timer.sec(); }
    void stopWriting() { m_timer.stop(); m_writing = m_timer.sec(); }
    void print( std::ostream& os, const kvs::Indent& indent = kvs::Indent(0) ) const
    {
        os << indent << "Reading time : " << m_reading << " [sec]" << std::endl;
        os << indent << "Exporting time : " << m_exporting << " [sec]" << std::endl;
        os << indent << "Writing time : " << m_writing << " [sec]" << std::endl;
    }
};

/*===========================================================================*/
/**
 *  @brief  Check whether the specified name is a dirctory or nor.
 *  @param  name [in] filename or directory name
 *  @return true if the specified name is a directory
 */
/*===========================================================================*/
bool IsDirectory( const std::string& name )
{
    kvs::Directory dir( name );
    return dir.isDirectory();
}

/*===========================================================================*/
/**
 *  @brief  Returns output filename with path.
 *  @param  dirname [in] output directory name
 *  @param  file [in] input filename
 *  @return output filename with path
 */
/*===========================================================================*/
std::string OutputFilename( const std::string& dirname, const kvs::File& file )
{
    const std::string basename = file.baseName();
    const std::string extension = ".fld";
    return dirname + kvs::File::Separator() + basename + extension;
}

/*===========================================================================*/
/**
 *  @brief  Returns output filename with path.
 *  @param  dirname [in] output directory name
 *  @param  directory [in] input directory name
 *  @param  ndivs [in] number of divisions of the output data file
 *  @return output filename with path
 */
/*===========================================================================*/
std::string OutputFilename( const std::string& dirname, const kvs::Directory& directory, const size_t ndivs )
{
    const std::string basename = directory.directoryName();
    const std::string extension = ndivs > 0 ? "" : ".fld";
    return dirname + kvs::File::Separator() + basename + extension;
}

}

/*===========================================================================*/
/**
 *  @brief  Main converting process.
 *  @param  argc [in] argument count
 *  @param  argv [in] argument values
 *  @return 0 (true) if the process is successfully done.
 */
/*===========================================================================*/
int Converter::exec( int argc, char** argv )
{
    Argument arg( argc, argv );
    if ( !arg.parse() ) return 1;

    const bool verbose = arg.hasVerboseOption();
    const kvs::Indent indent = kvs::Indent(4);
    if ( verbose ) arg.print( std::cout << "INPUT VARIABLE:" << std::endl, indent );

    Timer timer;
    bool retval = false;
    if ( ::IsDirectory( arg.input() ) )
    {
        // In case that the input is the name of a directory, the dataset will
        // be loaded as a SPH file list from the directory.
        timer.start();
        const std::string dirname = arg.input();
        const bool header_only = true;
        const kvsbloodvis::util::SPHFileList sph_list( dirname, header_only );
        if ( verbose ) sph_list.print( std::cout << "SPH FILE LIST:" << std::endl, indent );
        timer.stopReading();

        // The dataset will be exported to the AVS field structure in the main
        // memory. Note that the PC with a large-size of main memory is required
        // to process the large-scale dataaset. Because all of the dataset will
        // be temporary stored in the main memory.
        timer.start();
        const bool byte = sph_list.svType() == 1 && arg.hasByteOption();
        kvs::AVSField fld = kvsbloodvis::util::Export( sph_list, byte );
        if ( verbose ) fld.print( std::cout << "AVS FIELD DATA:" << std::endl, indent );
        timer.stopExporting();

        // The dataset will be output as AVS filed format to a file. If the
        // number of divisions 'ndivs' is specified, the dataset will be saved
        // as several subdivided files.
        timer.start();
        const size_t ndivs = arg.numberOfDivisions();
        const std::string output = ::OutputFilename( arg.output(), kvs::Directory( dirname ), ndivs );
        if ( verbose ) std::cout << "OUTPUT DATA:" << std::endl;
        retval = kvsbloodvis::util::Write( fld, output, ndivs, verbose, indent );
        timer.stopWriting();
    }
    else
    {
        // In case that the input is the name of a file, the dataset will
        // be loaded as a single SPH file.
        timer.start();
        const std::string filename = arg.input();
        const bool header_only = false;
        const kvsbloodvis::util::SPHFile sph( filename, header_only );
        if ( verbose ) sph.print( std::cout << "SPH FILE:" << std::endl, indent );
        timer.stopReading();

        // The dataset will be exported to the AVS field structure in the main
        // memory.
        timer.start();
        const bool byte = sph.svType() == 1 && arg.hasByteOption();
        kvs::AVSField fld = kvsbloodvis::util::Export( sph, byte );
        if ( verbose ) fld.print( std::cout << "AVS FIELD DATA:" << std::endl, indent );
        timer.stopExporting();

        // The dataset will be output as AVS filed format to a file.
        timer.start();
        const std::string output = ::OutputFilename( arg.output(), kvs::File( filename ) );
        if ( verbose ) std::cout << "OUTPUT DATA:" << std::endl;
        retval = kvsbloodvis::util::Write( fld, output, verbose, indent );
        timer.stopWriting();
    }

    if ( verbose ) timer.print( std::cout << "PROCESSING TIME:" << std::endl, indent );

    return retval;
}
